package com.whcoding.task.xxl.job.task.base;

import com.whcoding.task.xxl.job.task.utils.ThreadSettingUtils;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * tasker的监控中心
 *
 * @author whcoding
 * @description tasker的监控中心
 * @date 2023/4/06 9:10
 */
public class Monitor {
    private static final Logger logger = LoggerFactory.getLogger(Monitor.class);

    Monitor(BaseTasker tasker) {
        this.tasker = tasker;
        if (!tasker.getSetting().isPrintMonitorInfo()) {
            return;
        }
        ThreadSettingUtils.getSingleThreadExecutor("tasker-monitor-%d").execute(() -> {
            try {
                logger.info("tasker-monitor=[{}]", info().toString());
                TimeUnit.SECONDS.sleep(tasker.getSetting().getPrintMonitorInterval());
            } catch (Exception e) {
                logger.error("tasker-monitor-error", e);
            }
        });
    }

    private BaseTasker tasker;
    /**
     * 成功任务的总数
     */
    private AtomicLong taskSuccessNum = new AtomicLong(0);
    /**
     * 最终成功的总数
     */
    private AtomicLong taskRetrySuccessNum = new AtomicLong(0);
    /**
     * 失败任务的总数
     */
    private AtomicLong taskFailedNum = new AtomicLong(0);
    /**
     * 任务执行的最小时间(毫秒)
     */
    private AtomicLong taskMinTime = new AtomicLong(10000000);
    /**
     * 任务执行的最大时间(毫秒)
     */
    private AtomicLong taskMaxTime = new AtomicLong(0);
    /**
     * 任务执行的平均时间(毫秒)
     */
    private AtomicLong taskAvgTime = new AtomicLong(0);
    /**
     * 第一个任务开始时间
     */
    private Date taskBeginTime;
    /**
     * 最后一个任务开始时间
     */
    private Date taskLastTime;
    /**
     * 异常次数记录
     */
    private Map<String, AtomicInteger> errorMapping = new ConcurrentHashMap();
    /**
     * 不同重试次数的数量
     */
    private Map<Integer, AtomicLong> retryMapping = new ConcurrentHashMap();

    /**
     * 当前tasker状态的封装类
     *
     * @author kly
     * @description
     * @date 2021/7/22 14:04
     * @return
     */
    @Data
    @Accessors(chain = true)
    class Info {
        private Integer threadNum;
        private Long taskTotalNum;
        private Long taskSuccessNum;
        private Long taskRetrySuccessNum;
        private Long taskFailedNum;
        private Long taskMinTime;
        private Long taskMaxTime;
        private Long taskAvgTime;
        private Date taskBeginTime;
        private Date taskLastTime;
        private Map<String, AtomicInteger> errorMapping;
        private Map<Integer, AtomicLong> retryMapping;
    }

    /**
     * 获取当前tasker状态
     *
     * @param
     * @return com.crt.platform.module.cms.tasker.Monitor.Info
     * @description 获取当前tasker状态
     * @author whcoding
     * @date 2023/4/06 9:10
     */
    public Info info() {
        return new Info()
                .setThreadNum(tasker.getExecutor().getActiveCount())
                .setTaskTotalNum(taskSuccessNum.longValue() + taskFailedNum.longValue())
                .setTaskSuccessNum(taskSuccessNum.longValue())
                .setTaskRetrySuccessNum(taskRetrySuccessNum.longValue())
                .setTaskFailedNum(taskFailedNum.longValue())
                .setTaskMinTime(taskMinTime.longValue())
                .setTaskMaxTime(taskMaxTime.longValue())
                .setTaskAvgTime(taskAvgTime.longValue())
                .setTaskBeginTime(taskBeginTime)
                .setTaskLastTime(taskLastTime)
                .setErrorMapping(errorMapping)
                .setRetryMapping(retryMapping);
    }

    /**
     * 任务执行成功的通知
     *
     * @param success 任务执行是否成功
     * @param time    任务耗时(毫秒)
     * @return void
     * @description 任务执行成功的通知
     * @author whcoding
     * @date 2023/4/06 9:10
     */
    public void taskFinishNotice(boolean success, long time) {
        taskFinishNotice(success, time, 0, null);
    }

    /**
     * 任务执行成功的通知
     *
     * @param success    任务执行是否成功
     * @param time       任务耗时(毫秒)
     * @param retryTimes 任务当前是第几次重试
     * @return void
     * @description 任务执行成功的通知
     * @author whcoding
     * @date 2023/4/06 9:10
     */
    public void taskFinishNotice(boolean success, long time, int retryTimes) {
        taskFinishNotice(success, time, retryTimes, null);
    }

    /**
     * 任务执行成功的通知
     *
     * @param success 任务执行是否成功
     * @param time    任务耗时(毫秒)
     * @param e       任务抛出的异常
     * @return void
     * @description 任务执行成功的通知
     * @author whcoding
     * @date 2023/4/06 9:10
     */
    public void taskFinishNotice(boolean success, long time, Exception e) {
        taskFinishNotice(success, time, 0, e);
    }

    /**
     * 任务执行成功的通知
     *
     * @param success    任务执行是否成功
     * @param time       任务耗时(毫秒)
     * @param retryTimes 任务当前是第几次重试
     * @param e          任务抛出的异常
     * @return void
     * @description 任务执行成功的通知
     * @author whcoding
     * @date 2023/4/06 9:10
     */
    public void taskFinishNotice(boolean success, long time, int retryTimes, Exception e) {
        try {
            this.updateTaskNum(success, retryTimes);
            this.updateTaskTime(success, time);
            this.updateErrorMapping(e);
            this.updateRetryMapping(retryTimes);
        } catch (Exception exception) {
            exception.printStackTrace();
            logger.error("记录任务状态失败!");
        }
    }

    /**
     * 更新tasker中时间相关的指标数据
     *
     * @param success 任务执行是否成功
     * @param time
     * @return void
     * @description 第一个任务时间、最后一个任务时间、任务执行最短时间、任务执行最长时间、任务执行平均时间
     * @author whcoding
     * @date 2023/4/06 9:10
     */
    private void updateTaskTime(boolean success, long time) {
        taskLastTime = new Date();
        if (null == taskBeginTime) {
            taskBeginTime = new Date();
        }
        if (!success) {
            return;
        }
        if (taskSuccessNum.longValue() == NumberUtils.LONG_ZERO) {
            taskAvgTime.set(time);
        } else {
            taskAvgTime.set((taskAvgTime.longValue() * taskSuccessNum.longValue() + time) / taskSuccessNum.longValue());
        }
        if (time < taskMinTime.longValue()) {
            taskMinTime.set(time);
        }
        if (time > taskMaxTime.longValue()) {
            taskMaxTime.set(time);
        }
    }


    /**
     * 更新tasker中数量相关的指标数据
     *
     * @param success    任务是否执行成功
     * @param retryTimes 重试次数
     * @return void
     */
    private void updateTaskNum(boolean success, int retryTimes) {
        if (success) {
            taskSuccessNum.addAndGet(1);
            if (retryTimes > 0) {
                taskRetrySuccessNum.addAndGet(1);
            }
        } else {
            taskFailedNum.addAndGet(1);
        }
    }

    /**
     * 更新tasker中的重试次数mapping
     *
     * @param retryTimes 重试次数
     * @return void
     * @description 更新tasker中的重试次数mapping
     * @author whcoding
     * @date 2023/4/06 9:10
     */
    private void updateRetryMapping(int retryTimes) {
        if (retryTimes == 0) {
            return;
        }
        AtomicLong atomicLong = retryMapping.get(retryTimes);
        if (null == atomicLong) {
            retryMapping.put(retryTimes, new AtomicLong(1));
        } else {
            atomicLong.addAndGet(1);
        }
    }

    /**
     * 更新tasker中的重试次数mapping
     *
     * @param e 失败的异常
     * @return void
     * @description 更新tasker中的重试次数mapping
     * @author whcoding
     * @date 2023/4/06 9:10
     */
    private void updateErrorMapping(Exception e) {
        if (null == e) {
            return;
        }
        String errorMsg = e.getClass().getName();
        AtomicInteger errorNum = errorMapping.get(errorMsg);
        if (null == errorMapping.get(errorMsg)) {
            errorMapping.put(errorMsg, new AtomicInteger((1)));
        } else {
            errorNum.addAndGet(1);
        }
    }

}
